package remotecall.streamacceptor;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Pair;

import com.zjw.zy.utils.GsonUtils;
import com.zjw.zy.utils.LogUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import remotecall.RemoteCallManager;

/**
 * @author ：zhong.jw
 * @date ：Created in 2022/4/8 14:45
 * 分段传输接收器
 */
final class StreamAcceptor {

    private static final String TAG = "StreamAcceptor";

    /**
     * 服务端byte流存储超时时间
     */
    private static final int TIME_OUT = 10000;
    /**
     * byte流单次传输大小
     */

    private static volatile StreamAcceptor SERVICE = null;
    /**
     * binder access
     */
    private final String mDescriptor;

    /**
     * core binder
     */
    private final IByteBinder mBinder;


    private Handler mHandler;

    private HandlerThread mThread;

    private final Map<String, Pair<Integer, byte[]>> mSendCache = new ConcurrentHashMap<>();
    private String mJson;

    static StreamAcceptor open(IBinder binder, String descriptor) {
        return new StreamAcceptor(binder, descriptor);
    }

    static StreamAcceptor openInService() {
        if (SERVICE == null) {
            synchronized (StreamAcceptor.class) {
                if (SERVICE == null) {
                    SERVICE = new StreamAcceptor(null, null);
                }
            }
        }
        return SERVICE;
    }

    StreamAcceptor(IBinder binder, String descriptor) {
        mDescriptor = descriptor;
        mBinder = IByteBinder.Stub.asInterface(binder, descriptor);
    }


    String getDescriptor() {
        return mDescriptor;
    }

    /**
     * @return true: 需要缓存 else 不需要缓存
     */
    @ServiceApi
    boolean cacheBytesIfNeed(String key, Object target) {
        initThreadInServiceIfNeed();
        // 检查数据量
        if (target != null) {
            byte[] bytes = GsonUtils.toJson(target).getBytes(java.nio.charset.StandardCharsets.UTF_8);
            int count = bytes.length / RemoteCallManager.getMaxBytesLimit();
            if (count != 0) {
                mSendCache.put(key, new Pair<>(0, bytes));
                // 超时后清空缓存
                mHandler.postDelayed(() -> {
                    mSendCache.remove(key);
                }, RemoteCallManager.getBytesTimeOut());
                return true;
            }
        }
        return false;
    }

    /**
     * PAL层服务端使用，获取缓存数据
     */
    @ServiceApi
    synchronized byte[] waitBytesForService(String key) {
        if (!mSendCache.containsKey(key)) {
            return null;
        }

        Pair<Integer, byte[]> pair = mSendCache.get(key);
        if (pair == null) {
            return null;
        }
        Integer index = pair.first;
        byte[] bytes = pair.second;

        if (index == null || bytes == null) {
            return null;
        }

        int bytesLimit = RemoteCallManager.getMaxBytesLimit();
        int end = Math.min(bytes.length, (1 + index) * bytesLimit);
        byte[] result = new byte[end - index * bytesLimit];
        for (int i = index * bytesLimit, j = 0; i < end; i++, j++) {
            result[j] = bytes[i];
        }
        //更新缓存
        if (end == bytes.length) {
            mSendCache.remove(key);
        } else {
            pair = new Pair<>(index + 1, bytes);
            mSendCache.put(key, pair);
        }
        return result;
    }

    /**
     * 同步调用
     */
    @ClientApi
    <T> T acceptBytes(String key, Class<T> clazz) {
        if (mBinder == null) {
            LogUtils.e(TAG, "mBinder == null");
            return null;
        }
        return acceptBytesInner(key, clazz, str -> {
            try {
                return mBinder.waitBytes(str);
            } catch (RemoteException e) {
                LogUtils.e(TAG, e.getMessage());
            }
            return new byte[]{};
        });
    }

    private void initThreadInServiceIfNeed() {
        if (mHandler != null) {
            return;
        }
        mThread = new HandlerThread("StreamAcceptor Service Handler");
        mThread.start();
        mHandler = new Handler(mThread.getLooper());
    }

    private void closeThreadInService() {
        if (mHandler == null) {
            return;
        }
        mHandler = null;
        mThread.quitSafely();
        mThread = null;
    }

    private <T> T createImpl(byte[] data, Class<T> clazz) {
        mJson = new String(data, java.nio.charset.StandardCharsets.UTF_8);
        return GsonUtils.fromJson(mJson, clazz);
    }

    private <T> T acceptBytesInner(String key, Class<T> clazz, IStream stream) {

        LogUtils.d(TAG, "acceptBytesInner : " + key);
        try {
            List<byte[]> tempList = new ArrayList<>();
            int size = 0;
            byte[] result = stream.waitForBytes(key);

            while (result != null) {
                //分段传输详情数据
                size += result.length;
                tempList.add(result);
                result = stream.waitForBytes(key);
            }

            result = new byte[size];
            int index = 0;
            for (byte[] bytes : tempList) {
                for (byte aByte : bytes) {
                    result[index++] = aByte;
                }
            }
            return createImpl(result, clazz);
        } catch (Exception e) {
            LogUtils.e(TAG, "acceptBytesInner" + e.getMessage());
        }

        throw new RuntimeException("can't acceptBytesInner : key = " + key);
    }

    /**
     * 回收
     */
    void recycler() {
        mJson = null;
        mSendCache.clear();
        closeThreadInService();
    }

    public String getJson() {
        return mJson;
    }

    /**
     * ByteBinderImpl 实现接口
     */
    interface IStream {
        byte[] waitForBytes(String key);
    }


    @Target(value = ElementType.METHOD)
    @Retention(value = RetentionPolicy.SOURCE)
    private @interface ServiceApi {
        //服务端使用
    }


    @Target(value = ElementType.METHOD)
    @Retention(value = RetentionPolicy.SOURCE)
    private @interface ClientApi {
        //客户端使用
    }
}
